// Copyright 2007-2008 Thiago H. de Paula Figueiredo
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package br.com.arsmachina.dao.hibernate;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import br.com.arsmachina.dao.ReadableDAO;
import br.com.arsmachina.dao.SortCriterion;
/**
* {@link ReadableDAO} implementation using Hibernate. All methods use {@link #getSession()} to get
* {@link Session}.
*
* @author Thiago H. de Paula Figueiredo
* @param <T> the entity class related to this DAO.
* @param <K> the type of the field that represents the entity class' primary key.
*/
public abstract class ReadableDAOImpl<T, K extends Serializable> extends BaseHibernateDAO<T, K>
implements ReadableDAO<T, K> {
/**
* A {@link SortCriterion} array with no elements.
*/
final public static SortCriterion[] EMPTY_SORTING_CRITERIA = new SortCriterion[0];
final private String defaultHqlOrderBy = toHqlOrderBy(getDefaultSortCriteria());
/**
* Returns a HQL <code>order by</code> clause given some {@link SortCriterion}s.
*
* @param sortCriteria {@link SortCriterion} instances.
* @return a {@link String}.
*/
final public static String toHqlOrderBy(SortCriterion... sortCriteria) {
String string = "";
if (sortCriteria.length > 0) {
StringBuilder builder = new StringBuilder(" ORDER BY ");
for (int i = 0; i < sortCriteria.length - 1; i++) {
builder.append(sortCriteria.toString());
builder.append(", ");
}
builder.append(sortCriteria[sortCriteria.length - 1]);
string = builder.toString();
}
return string;
}
/**
* Constructor that takes a {@link Class} and a {@link SessionFactory}.
*
* @param clasz a {@link Class}.
* @param sessionFactory a {@link SessionFactory}. It cannot be null.
*/
@SuppressWarnings("unchecked")
public ReadableDAOImpl(SessionFactory sessionFactory) {
super(null, sessionFactory);
}
/**
* Constructor that takes a {@link Class} and a {@link SessionFactory}.
*
* @param clasz a {@link Class}.
* @param sessionFactory a {@link SessionFactory}. It cannot be null.
*/
@SuppressWarnings("unchecked")
public ReadableDAOImpl(Class<T> clasz, SessionFactory sessionFactory) {
super(clasz, sessionFactory);
}
/**
* @see br.com.arsmachina.dao.ReadableDAO#countAll()
*/
public int countAll() {
final Criteria criteria = createCriteria();
criteria.setProjection(Projections.rowCount());
return (Integer) criteria.uniqueResult();
}
/**
* Returns all the entity class' objects. They are sorted according to
* {@link #getDefaultSortCriterions()}.
*
* @see br.com.arsmachina.dao.ReadableDAO#findAll()
* @see #getDefaultSortCriterions()
*/
@SuppressWarnings("unchecked")
public List<T> findAll() {
Criteria criteria = createCriteria();
addSortCriteria(criteria, getDefaultSortCriteria());
return criteria.list();
}
/**
* @see br.com.arsmachina.dao.ReadableDAO#findById(java.io.Serializable)
*/
@SuppressWarnings("unchecked")
public T findById(K id) {
return (T) getSession().get(getEntityClass(), id);
}
/**
* @see br.com.arsmachina.dao.ReadableDAO#findByIds(K[])
*/
@SuppressWarnings("unchecked")
public List<T> findByIds(K... ids) {
Criteria criteria = createCriteria();
criteria.add(Restrictions.in(getPrimaryKeyPropertyName(), ids));
return criteria.list();
}
/**
* @see br.com.arsmachina.dao.ReadableDAO#findByExample(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public List<T> findByExample(T example) {
Criteria criteria = createCriteria();
if (example != null) {
criteria.add(createExample(example));
}
return criteria.list();
}
/**
* @see br.com.arsmachina.dao.WriteableDAO#refresh(java.lang.Object)
*/
public void refresh(T object) {
getSession().refresh(object);
}
/**
* If <code>sortingConstraints</code> is <code>null</code> or empty, this implementation
* sort the results by the {@link SortCriterion}s returned by
* {@link #getDefaultSortCriterions()}.
*
* @see br.com.arsmachina.dao.ReadableDAO#findAll(int, int,
* br.com.arsmachina.dao.SortCriterion[])
*/
@SuppressWarnings("unchecked")
public List<T> findAll(int firstResult, int maximumResults, SortCriterion... sortingConstraints) {
Criteria criteria = createCriteria();
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maximumResults);
if (sortingConstraints == null || sortingConstraints.length == 0) {
sortingConstraints = getDefaultSortCriteria();
}
addSortCriteria(criteria, sortingConstraints);
return criteria.list();
}
/**
* Reattaches the object to the current {@link org.hibernate.Session} using
* <code>Session.lock(object, LockMode.NONE)</code> and then returns the object.
*
* @param a <code>T</code>.
* @return <code>object</code>.
* @see br.com.arsmachina.dao.ReadableDAO#reattach(java.lang.Object)
*/
public T reattach(T object) {
getSession().lock(object, LockMode.NONE);
return object;
}
/**
* Adds <code>sortCriteria</code> to a {@link Criteria} instance.
*
* @param criteria a {@link Criteria}. It cannot be null.
* @param sortCriteria a {@link SortCriterion}<code>...</code>. It cannot be null.
* @todo Support for property paths, not just property names.
*/
final public void addSortCriteria(Criteria criteria, SortCriterion... sortCriteria) {
assert criteria != null;
if (sortCriteria == null || sortCriteria.length == 0) {
sortCriteria = getDefaultSortCriteria();
}
for (SortCriterion sortingConstraint : sortCriteria) {
final String property = sortingConstraint.getProperty();
final boolean ascending = sortingConstraint.isAscending();
final Order order = ascending ? Order.asc(property) : Order.desc(property);
criteria.addOrder(order);
}
}
/**
* Adds the default sort criteria to a {@link Criteria} instance. This method just does
* <code>addSortCriteria(criteria, getDefaultSortCriteria());</code>.
*
* @param criteria a {@link Criteria}. It cannot be null.
*/
protected void addSortCriteria(Criteria criteria) {
addSortCriteria(criteria, getDefaultSortCriteria());
}
/**
* Returns the default {@link SortCriterion}s to be used to sort the objects lists returned by
* methods like {@link #findAll()} and {@link #findAll(int, int, SortCriterion...)} when no
* sorting constraints are given. This implementation returns {@link #EMPTY_SORTING_CRITERIA}.
*
* @return a {@link SortCriterion} array. It cannot be <code>null</code>.
*/
public SortCriterion[] getDefaultSortCriteria() {
return EMPTY_SORTING_CRITERIA;
}
/**
* Creates a {@link Criteria} for this entity class.
*
* @return a {@link Criteria}.
*/
public Criteria createCriteria() {
return getSession().createCriteria(getEntityClass());
}
/**
* Creates a {@link Criteria} for this entity class with given sort criteria.
*
* @return a {@link Criteria}.
*/
public Criteria createCriteria(SortCriterion ... sortCriteria) {
Criteria criteria = createCriteria();
addSortCriteria(criteria, sortCriteria);
return criteria;
}
/**
* Creates a {@link Criteria} for this entity class with given sort criteria,
* first result index and maximum number of results.
*
* @return a {@link Criteria}.
*/
public Criteria createCriteria(int firstIndex, int maximumResults, SortCriterion ... sortCriteria) {
Criteria criteria = createCriteria(sortCriteria);
criteria.setFirstResult(firstIndex);
criteria.setMaxResults(maximumResults);
return criteria;
}
/**
* Used by {@link #findByExample(Object)} to create an {@link Example} instance.
*
* @todo add criteria for property types not handled by Example (primary keys, associations,
* etc)
* @return an {@link Example}.
*/
public Example createExample(T entity) {
Example example = Example.create(entity);
example.enableLike(MatchMode.ANYWHERE);
example.excludeZeroes();
example.ignoreCase();
return example;
}
/**
* Returns the value of the <code>defaultHqlOrderBy</code> property.
*
* @return a {@link String}.
*/
public final String getDefaultHqlOrderBy() {
return defaultHqlOrderBy;
}
}